﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Xml;

namespace AppDesigner.XamlDom
{
    #region XamlPropertyValue

    /// <summary>
    /// presist the XamlProperty Value
    /// </summary>
    public abstract class XamlPropertyValue
    {
        private XamlProperty _parentProperty;

        public XamlProperty ParentProperty
        {
            get { return _parentProperty; }
            set
            {
                if (value != _parentProperty)
                {
                    _parentProperty = value;
                    OnParentPropertyChanged();
                }
            }
        }

        public event EventHandler ParentPropertyChanged;

        public virtual void OnParentPropertyChanged()
        {
            if (ParentPropertyChanged != null)
            {
                ParentPropertyChanged(this, EventArgs.Empty);
            }
        }

        #region Abstract method

        public abstract object GetValueFor(XamlPropertyInfo targetProperty);
        public abstract void RemoveNodeFromParent();
        public abstract void AddNodeTo(XamlProperty property);
        public abstract XmlNode GetNodeForCollection();

        #endregion

    }
    #endregion


    #region XamlTextValue

    public class XamlTextValue : XamlPropertyValue
    {
        private XamlDocument _document;
        private XmlAttribute _attribute;
        private XmlText _textNode;
        private XmlSpace _currentXmlSpace;
        private string _textValue;
        private XmlCDataSection _cData;

        public XamlTextValue(XamlDocument document, XmlAttribute attribute)
        {
            _document = document;
            _attribute = attribute;
        }

        public XamlTextValue(XamlDocument document, string textValue)
        {
            _document = document;
            _textValue = textValue;
        }

        public XamlTextValue(XamlDocument document, XmlText textNode, XmlSpace xmlSpace)
        {
            this._document = document;
            this._textNode = textNode;
            this._currentXmlSpace = xmlSpace;
        }

        public XamlTextValue(XamlDocument document, XmlCDataSection cData, XmlSpace xmlSpace)
        {
            this._document = document;
            this._cData = cData;
            this._currentXmlSpace = xmlSpace;
        }

        public string Text
        {
            get
            {
                if (_attribute != null)
                {
                    if (_attribute.Value.StartsWith("{}", StringComparison.Ordinal))
                    {
                        return _attribute.Value.Substring(2);
                    }
                    else
                    {
                        return _attribute.Value;
                    }
                }
                else if(_textValue != null)
                {
                    return _textValue;
                }
                else if (_cData != null)
                {
                    return _cData.Value;
                }
                else
                {
                    return NormalizeWhitespace(_textNode.Value);
                }
            }
            set
            {
                if (value == null)
                {
                    throw new ArgumentNullException("value");
                }
                if (_attribute != null)
                {
                    _attribute.Value = value;
                }
                else if (_textValue != null)
                {
                    _textValue = value;
                }
                else if (_cData != null)
                {
                    _cData.Value = value;
                }
                else
                {
                    _textNode.Value = value;
                }
            }
        }

        private string NormalizeWhitespace(string text)
        {
            if (_currentXmlSpace == XmlSpace.Preserve)
            {
                return text.Replace("\r", "");
            }
            StringBuilder sb = new StringBuilder();
            bool wasWhitespace = true;
            foreach (char c in text)
            {
                if (char.IsWhiteSpace(c))
                {
                    if (!wasWhitespace)
                    {
                        sb.Append(' ');
                    }
                    wasWhitespace = true;
                }
                else
                {
                    wasWhitespace = false;
                    sb.Append(c);
                }
            }
            if (sb.Length > 0 & wasWhitespace)
            {
                sb.Length -= 1;
            }
            return sb.ToString();
        }

        public override object GetValueFor(XamlPropertyInfo targetProperty)
        {
            if (ParentProperty == null)
            {
                throw new InvalidOperationException("Can't call while ParentProperty is null.");
            }
            if (targetProperty == null)
            {
                return this.Text;
            }
            return XamlParser.Instance.CreateObjectFromAttributeText(Text, targetProperty, ParentProperty.ParentObject);
        }

        public override void RemoveNodeFromParent()
        {
            if (_attribute != null)
            {
                _attribute.OwnerElement.RemoveAttribute(_attribute.Name);
            }
            else if (_textNode != null)
            {
                _textNode.ParentNode.RemoveChild(_textNode);
            }
            else if (_cData != null)
            {
                _cData.ParentNode.RemoveChild(_cData);
            }
        }

        public override void AddNodeTo(XamlProperty property)
        {
            if (_attribute != null)
            {
                property.ParentObject.XmlElement.Attributes.Append(_attribute);
            }
            else if (_textValue != null)
            {
                _attribute = property.SetAttribute(_textValue);
                _textValue = null;
            }
            else if (_cData != null)
            {
                property.AddChildNodeToProperty(_cData);
            }
            else
            {
                property.AddChildNodeToProperty(_textNode);
            }
        }

        public override XmlNode GetNodeForCollection()
        {
            if (_textNode != null)
            {
                return _textNode;
            }
            else if (_cData != null)
            {
                return _cData;
            }
            else
            {
                throw new NotImplementedException();
            }
        }
    }

    #endregion
}
